/*
 *  Smtp.c
 *  MinimalSmtp
 *
 *  Created by Andrey on 22/12/2008.
 *  Copyright 2008 Karma Software. All rights reserved.
 *
 */

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

#include "../ClientDispatch/ClientInfo.h"
#include "../ClientDispatch/ClientDispatchLog.h"
#include "../ClientDispatch/ClientDispatch.h"
#include "../NetTalk/NetTalk.h"
#include "../Maildir/Maildir.h"
#include "Smtp.h"

#define MAX_COMMAND_TOKEN_COUNT 3

void ShowEnvelope(SmtpEnvelopePtr envelope)
{
	Log("Envelope %p", envelope);
	Log("Sender: %s@%s", envelope->sender.localPart, envelope->sender.domain);
	for (int i = 0; i < envelope->recipientCount; i++) 
	{
		Log("Recipient: %s@%s", envelope->recipients[i].localPart, envelope->recipients[i].domain);
	}
	Log("Message contents:");
	Log("%s", envelope->messageContents.data);
}

void PreparseDataFromClient(DataBuffer clientData)
{
	AsciiStringPtr dataAsText = (AsciiStringPtr)clientData.data;
	
	for (int i = 0; i < clientData.dataLength; i++) 
	{
		dataAsText[i] = tolower(dataAsText[i]);
		if (dataAsText[i] == CR || dataAsText[i] == LF)
			dataAsText[i] = 0;
	}
}

SmtpCommandHeaderPtr ParseDataFromClientManually(DataBuffer clientData)
{
	AsciiStringPtr commandText = (AsciiStringPtr)clientData.data;
	static AsciiChar delimiters[] = " :<>@";
	AsciiStringPtr commandTokens[MAX_COMMAND_TOKEN_COUNT];
	AsciiStringPtr currentToken;
	AsciiStringPtr* ptrToNextToken = &commandText;
	int i = 0;
	
	Log("ParseDataFromClientManually: parsing command text '%s'", commandText);
	
	while (currentToken = strsep(ptrToNextToken, delimiters)) 
	{
//			Log("ParseDataFromClientManually: current token is '%s'", currentToken);
		if (strcmp(currentToken, "") && strcmp(currentToken, "from") &&
			strcmp(currentToken, "to"))
		{
			Log("ParseDataFromClientManually: saving token '%s'", currentToken);
			commandTokens[i] = currentToken;
			i++;
		}
	}
	

	if (!strcmp(commandTokens[0], "ehlo"))
	{
		SmtpCommandWithDomainPtr commandWithDomain = 
			(SmtpCommandWithDomainPtr)calloc(1, sizeof(SmtpCommandWithDomain));
		commandWithDomain->header.type = Initiation;
		strncpy(commandWithDomain->domain, commandTokens[1], MAX_DOMAIN_LENGTH);
		
		return &commandWithDomain->header;
	}
	else if (!strcmp(commandTokens[0], "mail") || !strcmp(commandTokens[0], "rcpt"))
	{
		SmtpCommandWithAddressPtr commandWithAddress =
			(SmtpCommandWithAddressPtr)calloc(1, sizeof(SmtpCommandWithAddress));
		
		if (!strcmp(commandTokens[0], "mail"))
			commandWithAddress->header.type = MailTransactionStart;
		else if (!strcmp(commandTokens[0], "rcpt"))
			commandWithAddress->header.type = RecipientInfo;
		
		strncpy(commandWithAddress->address.localPart, commandTokens[1], MAX_USERNAME_LENGTH);
		strncpy(commandWithAddress->address.domain, commandTokens[2], MAX_DOMAIN_LENGTH);
		
		return &commandWithAddress->header;
	}
	else if (!strcmp(commandTokens[0], "rset"))
	{
		SmtpControlCommandPtr controlCommand = 
			(SmtpControlCommandPtr)calloc(1, sizeof(SmtpControlCommand));
		
		controlCommand->header.type = Reset;
		
		return &controlCommand->header;
	}
	else if (!strcmp(commandTokens[0], "noop"))
	{
		SmtpControlCommandPtr controlCommand = 
		(SmtpControlCommandPtr)calloc(1, sizeof(SmtpControlCommand));
		
		controlCommand->header.type = NoOperation;
		
		return &controlCommand->header;
	}
	else if (!strcmp(commandTokens[0], "data"))
	{
		SmtpControlCommandPtr controlCommand = 
		(SmtpControlCommandPtr)calloc(1, sizeof(SmtpControlCommand));
		
		controlCommand->header.type = MessageContents;
		
		return &controlCommand->header;
	}
	else if (!strcmp(commandTokens[0], "quit"))
	{
		SmtpControlCommandPtr controlCommand = 
		(SmtpControlCommandPtr)calloc(1, sizeof(SmtpControlCommand));
		
		controlCommand->header.type = Quit;
		
		return &controlCommand->header;
	}
	
	return NULL;
}

void WriteSmtpReplyToBuffer(DataBuffer replyBuffer, int replyCode)
{
	const char* replyText = GetSmtpReplyTextByCode(replyCode);
	
	if (replyText != NULL)
	{
		snprintf(replyBuffer.data, MAX_COMMAND_OR_REPLY_LENGTH, "%d %s", replyCode, replyText);
	}
}

enum SmtpEvent SmtpCommandTypeToFsmEvent(enum SmtpCommandType type)
{
	switch (type) 
	{
		case Initiation:
			return EHLO;
		case MailTransactionStart:
			return MAIL;
		case RecipientInfo:
			return RCPT;
		case MessageContents:
			return DATA;
		case NoOperation:
			return NOOP;
		case Reset:
			return RSET;
		case Quit:
			return QUIT;
		default:
			return NOOP;
	}
}

TRANSITION_HANDLER_START(OnStartListening)
	ResetBuffers;
TRANSITION_HANDLER_END

TRANSITION_HANDLER_START(OnStartWaitingForSenderAddress)
	switch (ev) 
	{
		case EHLO:
			if (previousState != Listening)
				ResetBuffers;
			SendReply(220);
			break;
		default:
			break;
	}
TRANSITION_HANDLER_END

TRANSITION_HANDLER_START(OnStartReceivingData)
	switch (event)
	{
		case DATA:
			SendReply(354);
			break;

	}
TRANSITION_HANDLER_END

TRANSITION_HANDLER_START(OnStopListening)
	switch (event)
	{
		case NOOP:
			SendReply(250);
			break;
		case QUIT:
			//CloseConnection;
			break;
	}
TRANSITION_HANDLER_END

TRANSITION_HANDLER_START(OnStopWaitingForSenderAddress)
	switch (event) 
	{
		case MAIL:
			StoreSender;
			SendReply(250);
			break;
		case RSET:
			SendReply(250);
			break;
		case NOOP:
			SendReply(250);
			break;
		case QUIT:
			//CloseConnection;
			break;
		default:
			break;
	}
TRANSITION_HANDLER_END

TRANSITION_HANDLER_START(OnStopWaitingForRecipientAddress)
	switch (event)
	{
		case EHLO:
			ResetBuffers;
		case RCPT:
			StoreRecipient;
			SendReply(250);
			break;
		case RSET:
			SendReply(250);
			break;
		case NOOP:
			SendReply(250);
			break;
		case QUIT:
			//CloseConnection;
			break;
		default:
			break;
	}
TRANSITION_HANDLER_END

TRANSITION_HANDLER_START(OnStopReceivingData)
	switch (event)
	{
		case TEXT:
			StoreMessageLine;
			SendReply(250);
			break;
		case END_TEXT:
			StoreEnvelope;
			SendReply(250);
			break;
		case RSET:
			SendReply(250);
			break;
		case NOOP:
			SendReply(250);
			break;
		case QUIT:
			//CloseConnection;
			break;
		default:
			break;
	}
TRANSITION_HANDLER_END